GO111MODULE=off, 這表示是GOPATH模式, 查找依賴包順序如同昨天提的vendor目錄和GOPATH下.
GO111MODULE=auto, 默認模式,在這模式下要使用go module, 需要滿足兩個條件
- 該專案目錄不在GOPATH/src/下
- 當前或上一層目錄存在go.mod檔案
有兩種方式能定義一個正確的Go module
// 在$GOPATH/src的目錄下, 建立合理的module路徑
// 進入該module目錄, 執行下面命令
go mod init [module name]
///
```bash
// 在任意地方, 建立好module路徑
// 在該目錄下, 執行
go mod init [folder/]module name
就會在該專案下生出了go.mod文件了.
module my/package
go 1.12
require other/thing v1.0.2 // 註解
require new/thing/v2 v2.3.4 // indirect
exclude old/thing v1.2.3
replace bad/thing v1.4.5 => good/thing v1.4.5
因為go.mod在專案的根目錄下, 子目錄的導入路徑會是該專案的導入路徑+子目錄路徑.
舉例: 建立了ithome的專案, 底下有一個ironman的子目錄.
則不需要也在子目錄建立go mod init指令, Go build會自動辨識ironman這目錄是ithome的一部分.
go get github.com/sirupsen/logrus
go.mod的內容
module modtest
go 1.12
require github.com/sirupsen/logrus v1.4.2 // indirect
此時把v1.4.2 改成v1.4.1
執行
go mod download
go.mod的內容
module modtest
go 1.12
require github.com/sirupsen/logrus v1.4.1 // indirect
也會發生$GOPATH/pkg/mod/github.com/sirupsen目錄下,多了logrus@v1.4.1和1.4.2版本的源碼
go.mod的內容
module modtest
go 1.12
require github.com/sirupsen/logrus v1.4.2 // indirect
exclude github.com/gin-gonic/gin v1.4.0
go get github.com/gin-gonic/gin
會發現應該是要下載當前最新板的v1.4.0的gin; 但因為有exclude gin 1.4.0 ;
所以改成下載v1.3.9
go.mod的內容
module modtest
go 1.12
require (
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.3.0 // indirect
github.com/golang/protobuf v1.3.2 // indirect
github.com/mattn/go-isatty v0.0.8 // indirect
github.com/sirupsen/logrus v1.4.2
github.com/ugorji/go v1.1.7 // indirect
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
)
exclude github.com/gin-gonic/gin v1.4.0
如果exclude指定gin的依賴功能包, 該功能包會避開該版號作安裝
如果有package被replace, 則編譯時會使用對應的項目來作取代.
gomodtest
// go.mod
module modtest
go 1.12
require github.com/sirupsen/logrus v1.4.2 // indirect
// modtest.go
package gomodtest
import (
log "github.com/sirupsen/logrus"
)
func Init() {
log.Info("godmodtest init")
}
func Exec() {
log.Info("godmodtest exec")
}
gomaintest
// go.mod
module github.com/tedmax100/gomaintest
go 1.12
replace github.com/tedmax100/modtest => ../gomodtest
// main.go
package main
import (
modtest "github.com/tedmax100/modtest"
)
func main() {
modtest.Exec()
}
執行結果
notes
目錄結構 /GOPATH/src/ithome
go mod init github.com/tedmax100/ithome
因為我等等要推上github的repo中, 這裡就如以前說的會有域名/目錄/專案...
這樣的層次關係.
go get github.com/sirupsen/logrus
這裡跟govendor fetch有些不同了, 再有go modules專案內輸入go get.
預設會去抓最新的tag版本; 如果沒有設立tag, 就抓最新的commit版本.
go.sum這時候就會把logrus目錄下go.mod跟go.sum的依賴包跟其版本保存起來.
go.sum 其實跟npm的package-lock.json有著一樣的功能.
go.mod(npm的package.json)定義我們指名要的依賴跟版本.
go.sum把go.mod的所有依賴包, 每一個像是樹的根節點一樣, 開始走訪去下載, 並且紀錄關係在此.
ironman/ironman.go
package ironman
import (
log "github.com/sirupsen/logrus"
)
func PrintIronMan() {
log.Info("hi iron man")
}
ithome.go
package ithome
import (
// 這裡因為我們定義的mod name就這麼長,
// 子目錄的導入路徑會是該專案的導入路徑+子目錄路徑.
"github.com/tedmax100/ithome/ironman"
log "github.com/sirupsen/logrus"
)
func PrintItHome() {
log.Info("hi ItHome")
ironman.PrintIronMan()
}
存檔, commit, 推上github.
這裡我沒有打release tag.
目錄結構 /GOPATH/src/gomod
go mod init gomod
// 下載依賴包
go get -u github.com/tedmax100/ithome
main.go
package main
import (
"github.com/tedmax100/ithome"
)
func main() {
ithome.PrintItHome()
}
執行main.go
// 作個更新
go get -u github.com/tedmax100/ithome
可以看到ithome這依賴包, 從本來是紀錄commit hash, 變成是紀錄tag版本號了.
// 作個更新
go get -u github.com/tedmax100/ithome
正如前面說的, 他會先找tag/release有沒有, 沒有才去找最新的commit.
但因為我們已經有tag v0.0.1, 所以怎樣更新依賴,
只要沒有更新版的依賴被release就不會被更新.
各版本有下載過得都會在go/pkg/mod/匯入包路徑底下
go get github.com/tedmax100/ithome@v0.0.1
因為快取有了, 就不必重抓
也會順便更改go.mod和go.sum的內容
或 我怕外部有人偷偷在代碼放後門, 我要用自己網路cache有的, 複製到vendor下
go mod vendor
這會建立出一個vendor目錄, 底下有現在go.mod依賴包的代碼.
我們改一下程式
gomod/vendor/github.com/tedmax100/ithome/ithome.go
package ithome
import (
"github.com/tedmax100/ithome/ironman"
log "github.com/sirupsen/logrus"
)
func PrintItHome() {
// 就改這行, 存檔
log.Info("hi ItHome from vendor")
ironman.PrintIronMan()
}
開心的在terminal輸入
go run main.go
笑XD
因為只要啟用了go modules, 就會完全忽略了vendor目錄的存在, 只讀取go.mod的內容.
那怎辦呢?
原本的指令go build, go install, go runm, go test啦
等等的加上-mod=vendor
go get github.com/go-sql-driver/mysql
結果最後根本沒有半個地方有import
怎辦, 自己檢查每一個.go檔案, 看哪些沒有import ?
哪些依賴又沒有抓到呢?
# add missing and remove unused module
go mod tidy
要是有breaking change, 新舊版本無法兼容呢?
ithome/go.mod
module github.com/tedmax100/ithome@v2.0.0 // 這裡打上版本號
go 1.12
require github.com/sirupsen/logrus v1.4.2
改個程式
package ithome
import (
"github.com/tedmax100/ithome/ironman"
log "github.com/sirupsen/logrus"
)
func PrintItHome() {
log.Info("hi ItHome V0.0.7")
ironman.PrintIronMan()
}
func PrintItHomeV2() {
log.Info("hi ItHome V2.0")
ironman.PrintIronMan()
}
存檔commit, push作release
跑到執行專案, 執行
go get -u github.com/tedmax100/ithome
這時候發現, 不會去下載這2.0.0版本的依賴包
因為版本號的v2.0.0, 這個第一個數字表示主版本號, 不同版本間若是無法兼容使用,
則建議是提昇這版本號, 且建議遠端分之多上v2分支.
版本號若是v1.10.13, 這個1表示主要版本號, 10表示次要版本號, 13表示修正版本號
且go get -u會檢查go mod的版本號, 並不會主動去下載並提昇到不同的主要版本號的依賴包.
這裡import改成使用v2版
package main
import (
"github.com/tedmax100/ithome/v2"
)
func main() {
ithome.PrintItHome()
ithome.PrintItHomeV2()
}
go mod tidy
開心了, 收工
go mod 可以相當完美的跟vendor做切換並存.
有機會來玩玩看goproxy.